//-----------------------------------------------------------------------------
// File: CreateDevice.cpp
//
// Desc: First test on a graphics engine
//
//-----------------------------------------------------------------------------
#include <d3d9.h>
#include <d3dx9.h>
#include <windows.h>
#include <strsafe.h>
#include <Windows.h>
#include <mmsystem.h>
#include "MyCam.h"

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D9				 g_pD3D = NULL; 
LPDIRECT3DDEVICE9		 g_pd3dDevice = NULL;
LPDIRECT3DVERTEXBUFFER9  g_pVB = NULL;
MyCam					 g_TestCam;


//-----------------------------------------------------------------------------
// Structures 
//-----------------------------------------------------------------------------

// Struct for custom vertex
struct CUSTOMVERTEX
{
	D3DXVECTOR3 position;
	D3DXVECTOR3 normal;
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Dec: Creates the D3D device
//-----------------------------------------------------------------------------

HRESULT InitD3D(HWND hWnd)
{
	if(NULL == (g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )))
		return E_FAIL;

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

	if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,
								 hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
								 &d3dpp, &g_pd3dDevice)))
	return E_FAIL;

	g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
	
	return S_OK;
}

//-----------------------------------------------------------------------------
// Name: InitVB()
// Dec: Initilizes the vertex buffer
//-----------------------------------------------------------------------------

HRESULT InitVB()
{

// Now we create our blank Vertex buffer
	if(FAILED(g_pd3dDevice->CreateVertexBuffer(50*2*sizeof(CUSTOMVERTEX), 0, 
											D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT, 
											&g_pVB, NULL)))
	return E_FAIL;

// Now we will the buffer
	CUSTOMVERTEX* pVerts;
	if(FAILED(g_pVB->Lock(0, 0, (void**)&pVerts, 0)))
		return E_FAIL;
// Generates the cylinder was capied from the lighting tutorial
	for( DWORD i = 0; i < 50; i++ )
    {
        FLOAT theta = ( 2 * D3DX_PI * i ) / ( 50 - 1 );
        pVerts[2 * i + 0].position = D3DXVECTOR3( sinf( theta ), -1.0f, cosf( theta ) );
        pVerts[2 * i + 0].normal = D3DXVECTOR3( sinf( theta ), 0.0f, cosf( theta ) );
        pVerts[2 * i + 1].position = D3DXVECTOR3( sinf( theta ), 1.0f, cosf( theta ) );
        pVerts[2 * i + 1].normal = D3DXVECTOR3( sinf( theta ), 0.0f, cosf( theta ) );
    }
	g_pVB->Unlock();

	return S_OK;
		
}

//-----------------------------------------------------------------------------
// Name: SetUpMatirx()
// Dec: Creates World, View and Projection Matrices
//-----------------------------------------------------------------------------

void SetUpMatrices()
{
// Set World Matrix and makes it spin
	D3DXMATRIX g_Transform;
	D3DXMatrixIdentity(&g_Transform);
	D3DXMatrixRotationX(&g_Transform, timeGetTime()/1000.0f);

	g_pd3dDevice->SetTransform(D3DTS_WORLD,&g_Transform);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, g_TestCam.GetViewMatrix());
}

void InitCam()
{
	D3DXVECTOR3 vEyePt(0.0f, 3.0f, -5.0f);
	D3DXVECTOR3 vLookAtPt(0.0f, 0.0f, 0.0f);
	// Set View Matrix
	g_TestCam.SetViewParams(&vEyePt, &vLookAtPt);
	g_pd3dDevice->SetTransform(D3DTS_VIEW, g_TestCam.GetViewMatrix());

// Set Projection Matrix
	float FOV = D3DX_PI/4;
	g_TestCam.SetProjParams(FOV ,1.0f, 1.0f, 100.0f);
	g_pd3dDevice->SetTransform(D3DTS_PROJECTION, g_TestCam.GetProjectionMatrix());
}

//-----------------------------------------------------------------------------
// Name: SetUpLights()
// Desc: Creates the lights
//-----------------------------------------------------------------------------
void SetUpLights()
{
// Sets up the material, only one material can be used right now
	D3DMATERIAL9 mtrl;
    ZeroMemory(&mtrl, sizeof(D3DMATERIAL9));
    mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
    mtrl.Diffuse.g = mtrl.Ambient.g = 0.0f;
    mtrl.Diffuse.b = mtrl.Ambient.b = 0.6f;
    mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
    g_pd3dDevice->SetMaterial(&mtrl);

// Setting up the Lights
	D3DXVECTOR3 vecDir;
	D3DLIGHT9 light;
	ZeroMemory(&light, sizeof(D3DLIGHT9));
	light.Type = D3DLIGHT_DIRECTIONAL;
	light.Diffuse.r = 1.0f;
	light.Diffuse.g = 1.0f;
	light.Diffuse.b = 1.0f;
// Rotates the directional light
    vecDir = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
	D3DXVec3Normalize((D3DXVECTOR3*)&light.Direction, &vecDir);
	light.Range = 1000.0f;
	g_pd3dDevice->SetLight(0,&light);
	g_pd3dDevice->LightEnable(0,TRUE);
	g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
// add ambient
	g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50,10,10));
}

//-----------------------------------------------------------------------------
// Name: Render()
// Dec: Draws the scene
//-----------------------------------------------------------------------------

VOID Render()
{
	if(NULL == g_pd3dDevice)
		return;

//Clears Back and Z buffer
	g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
						D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
	
//Check to see if we lost control of the graphics card
/*	HRESULT DevTest = g_pd3dDevice->TestCooperativeLevel();
	if(FAILED(DevTest))
	{
		if(DevTest == D3DERR_DEVICELOST)
			return;
		if(DevTest == D3DERR_DEVICENOTRESET)
		{
		//	DeleteMyResources();
		//	g_pd3dDevice->Reset();
		//	InityMyResources();
		}
	}*/
// Creates the scene
	if(SUCCEEDED(g_pd3dDevice->BeginScene()))
	{
		SetUpLights();
		g_TestCam.Update();
        // Setup the world, view, and projection matrices
        SetUpMatrices();
        // Render the vertex buffer contents
        g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof( CUSTOMVERTEX));
        g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
        g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2 * 50 - 2);
//Ends the scene
		g_pd3dDevice->EndScene();
	}
	g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
}

//-----------------------------------------------------------------------------
// Name: CleanUp()
// Desc: Draws the scene
//-----------------------------------------------------------------------------

void CleanUp()
{
	if(g_pD3D != NULL)
		g_pD3D->Release();
	if(g_pd3dDevice != NULL)
		g_pd3dDevice->Release();
	if(g_pVB != NULL)
		g_pVB->Release();
}

//-----------------------------------------------------------------------------
// Name: MsgProc()
// Dec: Processes windows messages
//-----------------------------------------------------------------------------

LRESULT CALLBACK MsgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	g_TestCam.HandleMessages(hWnd, message, wParam, lParam);
	switch(message)
	{
	case WM_DESTROY:
		CleanUp();
		PostQuitMessage(0);
		return 0;
		break;

	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

//-----------------------------------------------------------------------------
// Name: wWinMain()
// Dec: Creates the window
//-----------------------------------------------------------------------------

INT WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, LPWSTR, INT)
{
	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
					GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
					TEXT("Test"), NULL};
	RegisterClassEx(&wc);

	HWND hWnd = CreateWindow(TEXT("Test"), TEXT("First Engine Test"),
							WS_OVERLAPPEDWINDOW, 100, 100, 512, 512,
							GetDesktopWindow(), NULL, wc.hInstance, NULL);
	if(SUCCEEDED(InitD3D(hWnd)))
    {
		if(SUCCEEDED(InitVB()))
		{
			InitCam();
			ShowWindow(hWnd, SW_SHOWDEFAULT);
			UpdateWindow(hWnd);
			MSG msg;
			ZeroMemory(&msg, sizeof(msg));
			  while(msg.message != WM_QUIT)
			  {
				if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
				else
					Render();
		  }
		}
	}

	return 0;
}